package com.warmheart.core.combine.oper;

import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import javax.validation.constraints.NotNull;

import com.warmheart.core.co.builder.CoBuilder;
import com.warmheart.core.co.oper.Co;
import com.warmheart.core.combine.enums.CombineTypeEnum;
import com.warmheart.core.combine.model.JoinInfoModel;
import com.warmheart.core.combine.oper.Select;
import com.warmheart.core.cors.builder.CoRsBuilder;
import com.warmheart.core.ex.ServerException;
import com.warmheart.core.rs.model.SelectedModel;

/**
 * 
 * @ClassName: On
 * @Description: 该类用作对联合查询条件进行处理
 * @author ZhangJianyang
 * @date 2025-06-16 09:37:02
 *
 * @param <T>
 * @param <E>
 */
public class Combine {

    private final List<CoRsBuilder<?, ?>> joinList;

    private final Map<CoRsBuilder<?, ?>, String> joinAliasMap;

    private final Map<String, CoRsBuilder<?, ?>> aliasJoinMap;

    public Combine(List<CoRsBuilder<?, ?>> joinList, Map<CoRsBuilder<?, ?>, String> joinAliasMap,
            Map<String, CoRsBuilder<?, ?>> aliasJoinMap) {
        this.joinList = joinList;
        this.joinAliasMap = joinAliasMap;
        this.aliasJoinMap = aliasJoinMap;
    }

    public <J extends CoRsBuilder<JE, ?>, JE extends CoBuilder<J>> Combine next(J join,
            CombineTypeEnum... combineTypeEnums) {
        if (join == null) {
            throw new ServerException();
        }
        CombineTypeEnum combineTypeEnum = CombineTypeEnum.LEFT;
        if (combineTypeEnums != null && combineTypeEnums.length > 0 && combineTypeEnums[0] != null) {
            combineTypeEnum = combineTypeEnums[0];
        }
        int i = joinList.size();
        if (joinAliasMap.get(join) != null) {
            throw new ServerException();
        }
        String alias = "t" + (i > 0 ? i : "");
        joinAliasMap.put(join, alias);
        aliasJoinMap.put(alias, join);
        joinList.add(join);
        Map<String, JoinInfoModel> joinMap = this.joinList.get(0).getJoinModel().getJoinMap();
        JoinInfoModel joinInfoModel = new JoinInfoModel();
        joinInfoModel.setCombineType(combineTypeEnum.getValue());
        joinInfoModel.setTableName(join.tableName());
        joinInfoModel.setAliasName(alias);
        joinMap.put(alias, joinInfoModel);
        this.joinList.get(0).getJoinModel().getJoinInfoList().add(joinInfoModel);
        return new Combine(joinList, joinAliasMap, aliasJoinMap);
    }

    public <J extends CoRsBuilder<JE, ?>, JE extends CoBuilder<J>> Join<J, JE> join(J j) {
        if (joinAliasMap.get(j) == null) {
            throw new ServerException();
        }
        return new Join<J, JE>(joinList.get(0), j);
    }

    public <P extends CoRsBuilder<?, ?>> String aliasName(P p) {
        return this.joinAliasMap.get(p);
    }

    public CoRsBuilder<?, ?> mainT() {
        return this.joinList.get(0);
    }

    public <P extends CoRsBuilder<PE, ?>, PE extends CoBuilder<P>, J extends CoRsBuilder<JE, ?>, JE extends CoBuilder<J>> Where<P, PE, J, JE> where(
            @NotNull Supplier<Co<P, PE>> preSupplier, @NotNull Supplier<Co<J, JE>> joinSupplier) {
        return new Where<P, PE, J, JE>(this, preSupplier.get(), joinSupplier.get());
    }

    public <P extends CoRsBuilder<PE, ?>, PE extends CoBuilder<P>> SingleWhere<P, PE> where(
            @NotNull Supplier<Co<P, PE>> preSupplier) {
        return new SingleWhere<P, PE>(this, preSupplier.get());
    }

    public <P extends CoRsBuilder<PE, ?>, PE extends CoBuilder<P>> Order<P, PE> order(
            @NotNull Supplier<Co<P, PE>> preSupplier) {
        return new Order<P, PE>(this, preSupplier.get());
    }

    public Select select() {
        List<SelectedModel> selectList = this.mainT().getJoinModel().getSelectList();
        selectList.clear();
        this.mainT().getJoinModel().setMainAllSelectFlag(true);
        return new Select(this);
    }
    
    public Select select(boolean distinctFlag) {
        List<SelectedModel> selectList = this.mainT().getJoinModel().getSelectList();
        selectList.clear();
        this.mainT().getJoinModel().setMainAllSelectFlag(true);
        this.mainT().getJoinModel().getDistinctModel().setDistinctFlag(distinctFlag);
        return new Select(this);
    }
}
